# 理财组合优化器
import os
import Core.Config as Config
import pandas as pd
import numpy as np
import scipy.optimize as sco
from AssetAllocation.Markowitz import MinReturn, MinVariance, MinSharpe, Calc_Portfolio_Profile


# 计算加权风险等级
def calc_risk_level(weight_list, risk_level_list):
    sum_risk = 0
    for i in range(len(weight_list)):
        sum_risk += weight_list[i] * risk_level_list[i]
    #
    return sum_risk


# 计算权益敞口
def calc_equity_exposure(weight_list, equity_exposure_list):
    expo_threshold = 0.2
    sum_exposure = 0
    for i in range(len(weight_list)):
        equity_exposure = equity_exposure_list[i]
        if equity_exposure >= expo_threshold:  # 大于阈值，权重即为权益敞口
            sum_exposure += weight_list[i]
    #
    return sum_exposure


# 生成协方差矩阵
def calc_covariance_matrix(correlation, annual_std_dev):
    #
    num_assets = len(annual_std_dev)
    covariance = np.zeros([num_assets, num_assets])
    #
    for i in range(0, num_assets):
        for j in range(num_assets):
            if j < i:
                correlation[i][j] = correlation[j][i]
            if j == i:
                correlation[i][j] = 1
    #
    # print("Correlation")
    # df = pd.DataFrame(correlation)
    # print(df)

    # CoVariance = Rho * Asset1 SD * Asset2 SD
    for i in range(num_assets):
        for j in range(num_assets):
            covariance[i][j] = correlation[i][j] * annual_std_dev[i] * annual_std_dev[j]
            # print(i, j, "Pho", correlation[i][j], "sd-i", annual_std_dev[i], "sd-j", annual_std_dev[j], "res", covariance[i][j])

    #
    # print("Covariance")
    return covariance


def build_correlation(asset_type_list, df_corr):
    num_assets = len(asset_type_list)
    correlation = np.zeros([num_assets, num_assets])
    #
    for i in range(0, num_assets):
        for j in range(num_assets):
            asset_type_i = asset_type_list[i]
            asset_type_j = asset_type_list[j]
            #
            corr_tmp = df_corr.loc[asset_type_i, asset_type_j]
            if np.isnan(corr_tmp):
                corr_tmp = df_corr.loc[asset_type_j, asset_type_i]
            correlation[i][j] = corr_tmp
            if j < i:
                correlation[i][j] = correlation[j][i]
            if j == i:
                correlation[i][j] = 1
    #
    return correlation


def run_optimize():
    path_filename = "C:\\Users\FengShimeng\Documents\产品文档\区域款产品\资产列表.xlsx"
    df = pd.read_excel(path_filename, sheet_name="expected")
    #
    exp_returns = list(df["预期收益"])
    asset_type_list = list(df["资产类型"])
    symbol_list = list(df["Symbol"])
    num_assets = len(exp_returns)

    # 凤霞等级约束条件
    risk_level_list = list(df["风险等级"])
    target_risk = 2.4

    # 权益敞口约束条件
    equity_exposure_list = list(df["权益敞口"])
    target_equity_exposure = 0.09

    # 波动率
    df["预期波动率"] = df["预期波动率"] * 0.01
    annual_std_dev_list = list(df["预期波动率"])

    # 构建相关性矩阵
    df_corr = pd.read_excel(path_filename, sheet_name="corr")
    df_corr.set_index("资产类型", inplace=True)
    #
    correlation = build_correlation(asset_type_list, df_corr)
    covariance_matrix = calc_covariance_matrix(correlation, annual_std_dev_list)

    # cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    cons = [
        {'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
        {'type': 'eq', 'fun': lambda x: calc_risk_level(x, risk_level_list) - target_risk},
        {'type': 'eq', 'fun': lambda x: calc_equity_exposure(x, equity_exposure_list) - target_equity_exposure},
    ]

    print(" 收益最大 ")
    #
    bnds = tuple((0, 0.1) for x in range(num_assets))  # 单票持仓不超过10%
    # MinReturn, MinSharpe
    optv = sco.minimize(MinSharpe, num_assets * [1./num_assets, ],
                        args=(exp_returns, covariance_matrix, ),
                        method='SLSQP',
                        bounds=bnds,
                        constraints=cons)
    print(optv)

    # 打印结果
    weight_res = optv.x
    pf_position = np.sum(weight_res)
    pf_risk_level = calc_risk_level(weight_res, risk_level_list)
    pf_equity_expo = calc_equity_exposure(weight_res, equity_exposure_list)
    #
    proflie = Calc_Portfolio_Profile(weight_res, exp_returns, covariance_matrix, risk_free_rate=0.0)

    for i in range(num_assets):
        print(symbol_list[i], weight_res[i])

    print(pf_position, pf_risk_level, pf_equity_expo, "Return", proflie["Return"], "Volatility", proflie["Volatility"], "Sharpe", proflie["Sharpe"])



if __name__ == '__main__':
    #
    path_filename = os.getcwd() + "\..\Config\config_local.json"
    database = Config.create_database(database_type="MySQL", config_file=path_filename, config_field="MySQL")
    #
    run_optimize()